package org.zhaya.sdk.processors;

import com.google.common.collect.ImmutableMap;
import io.logz.sawmill.Doc;
import io.logz.sawmill.ProcessResult;
import io.logz.sawmill.Processor;
import io.logz.sawmill.annotations.ProcessorProvider;
import io.logz.sawmill.exceptions.ProcessorExecutionException;
import io.logz.sawmill.utilities.JsonUtils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Objects.requireNonNull;

/**
 * @author luka
 * @description syslogpri解析
 * @date 2022/9/12
 */
@ProcessorProvider(type = "syslogpri", factory = SyslogPriProcessor.Factory.class)
public class SyslogPriProcessor implements Processor {

    private static final Pattern SYSLOG_PATTERN = Pattern.compile("<(\\d+)>(\\w{3}\\s+\\d{1,2}\\s\\d\\d:\\d\\d:\\d\\d)\\s+([\\w-]+)\\s+([\\w-\\.\\]\\[]+):\\s(.*)");
    private static final Map<Integer, String> FACILITY = ImmutableMap.<Integer, String>builder()
            .put(0, "kernel messages")
            .put(1, "user-level messages")
            .put(2, "mail system")
            .put(3, "system daemons")
            .put(4, "security/authorization messages (note 1)")
            .put(5, "messages generated internally by syslogd")
            .put(6, "line printer subsystem")
            .put(7, "network news subsystem")
            .put(8, "UUCP subsystem")
            .put(9, "clock daemon (note 2)")
            .put(10, "security/authorization messages (note 1)")
            .put(11, "FTP daemon")
            .put(12, "NTP subsystem")
            .put(13, "log audit (note 1)")
            .put(14, "log alert (note 1)")
            .put(15, "clock daemon (note 2)")
            .put(16, "local use 0  (local0)")
            .put(17, "local use 1  (local1)")
            .put(18, "local use 2  (local2)")
            .put(19, "local use 3  (local3)")
            .put(20, "local use 4  (local4)")
            .put(21, "local use 5  (local5)")
            .put(22, "local use 6  (local6)")
            .put(23, "local use 7  (local7)").build();

    private static final Map<Integer, String> SEVERITY = ImmutableMap.<Integer, String>builder()
            .put(0, "Emergency: system is unusable")
            .put(1, "Alert: action must be taken immediately")
            .put(2, "Critical: critical conditions")
            .put(3, "Error: error conditions")
            .put(4, "Warning: warning conditions")
            .put(5, "Notice: normal but significant condition")
            .put(6, "Informational: informational messages")
            .put(7, "Debug: debug-level messages").build();

    private final String field;
    private final String facilityField;
    private final String severityField;

    public SyslogPriProcessor(String field, String facilityField, String severityField) {
        this.field = requireNonNull(field, "field cannot be null");
        this.facilityField = requireNonNull(facilityField, "facilityField cannot be null");
        this.severityField = requireNonNull(severityField, "severityField cannot be null");
    }

    @Override
    public ProcessResult process(Doc doc) {
        if (StringUtils.isBlank(field)) {
            return ProcessResult.failure(String.format("failed to process syslogpri, field in path [%s] is missing", field));
        }
        if (StringUtils.isBlank(facilityField)) {
            return ProcessResult.failure(String.format("failed to process syslogpri, facilityField in path [%s] is missing", facilityField));
        }
        if (StringUtils.isBlank(severityField)) {
            return ProcessResult.failure(String.format("failed to process syslogpri, severityField in path [%s] is missing", severityField));
        }
        try {
            Matcher matcher = SYSLOG_PATTERN.matcher(doc.getField(field));
            if (matcher.find()) {
                int pri = Integer.parseInt(matcher.group(1));
                doc.addField(facilityField, FACILITY.get(pri / 8));
                doc.addField(severityField, SEVERITY.get(pri % 8));
            }
        } catch (Exception e) {
            return ProcessResult.failure(String.format("failed to process doc to docs for field in path %s", field),
                    new ProcessorExecutionException("syslogpri", e));
        }
        return ProcessResult.success();
    }

    public static class Factory implements Processor.Factory {
        @Override
        public Processor create(Map<String, Object> config) {
            Configuration syslogPri = JsonUtils.fromJsonMap(Configuration.class, config);
            return new SyslogPriProcessor(syslogPri.getField(), syslogPri.getFacilityField(), syslogPri.getSeverityField());
        }
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Configuration implements Processor.Configuration {
        private String field;
        private String facilityField;
        private String severityField;
    }

}
